java基础学习—线程,java基础线程
java基础学习—线程,java基础线程
java线程
1、java线程的理解
线程就是程序执行代码块的过程:它会与各种状态,或者叫生命周期:创建、就绪(启动)、执行、阻塞、睡眠、结束(消亡),
上面这个顺序是不确定的
注意区分系统的进程与java的线程的区别,以及cpu时间片对java线程的影响
1.1 java虚拟机JVM是进程,在windows任务管理器中显示的java.exe
1.2 一个普通java程序启动后,main方法所在的线程是主线程,这时java后台还有一个线程:负责垃圾回收,释放内存空间
1.3 多线程:即多个线程同时运行。
特点:并发执行,提高效率,消耗资源较大
1.4 java创建线程是通过调用底层系统的接口来实现的
1.5 在java中创建线程有两种方式:一种是继承Thread类,另外一种是实现Runable接口
1.6 通过继承Thread方法创建线程:
*继承Thread类
*创建此对象,并调用start()方法
package thread;
public class TestThreadCreat{
public static void main(String[] args) {
ThreadCreat t1 = new ThreadCreat("threadCreat1");
ThreadCreat t2 = new ThreadCreat("threadCreat2");
t1.start();
t2.start();
}
}
class ThreadCreat extends Thread{
public ThreadCreat(String name){
super(name);
}
public void run(){
int a = 1;
while(a<=100){
System.out.println(this.getName()+"__"+a++);
}
}
}
1.7 通过实现Runable接口创建线程:
步骤:
*定义类实现Runnable接口
*覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中。
*通过Thread类建立线程对象。
*将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数。
因为,自定义的run方法所属的对象是Runnable接口的子类对象。
所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。
*调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
package thread;
public class TestRunnableCreate {
public static void main(String[] args) {
RunnableCreate runnableCreate1 = new RunnableCreate();
RunnableCreate runnableCreate2 = new RunnableCreate();
new Thread(runnableCreate1).start();
new Thread(runnableCreate1).start();
}
}
class RunnableCreate implements Runnable {
public void run(){
int a = 1;
while(a<=100){
System.out.println(Thread.currentThread().getName()+"__"+a++);
}
}
}
2 多线程的安全问题:当多个线程同时对相同的资源进行操作时,就有可能导致访问的资源不准确,这种情况就称之为线程安全问题。
package thread;
/*
* 不安全的多线程售票,可能出现负票
*/
public class TestThreadDanger {
public static void main(String[] args) {
Ticket t1 = new Ticket();
new Thread(t1,"TicketSaleMan1").start();
new Thread(t1,"TicketSaleMan2").start();
new Thread(t1,"TicketSaleMan3").start();
new Thread(t1,"TicketSaleMan4").start();
}
}
class Ticket implements Runnable {
private int tick = 100;
public void run(){
while(true){
if(tick>0){
try{Thread.sleep(20);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"__sale:"+tick--);
}
else{
break;
}
}
}
}
2.1 java中通过同步这个概念来解决线程安全问题
即,将多个线程共通操作的资源进行访问控制(锁/锁旗标),在某一个时刻只能一天线程进行操作。关键字是:syschronized
同步的前提:*两个及两个以上线程
*多个线程的访问控制使用同一个锁
2.2 java中同步有 种实现方式:同步代码块,同步函数(静态同步函数和非静态同步函数)
2.2.1 同步代码块:
将多线程共通执行的代码块放到同步代码块中执行,这样就可以通过同步代码块的锁进行访问控制。
如果一个线程先持有同步代码块的锁,则在它释放这个锁之前,其它现在只能在外面等待
package thread;
/*
*增加同步代码块后,这个程序是线程安全的
*/
public class TestThreadDanger {
public static void main(String[] args) {
Ticket t1 = new Ticket();
new Thread(t1, "TicketSaleMan1").start();
new Thread(t1, "TicketSaleMan2").start();
new Thread(t1, "TicketSaleMan3").start();
new Thread(t1, "TicketSaleMan4").start();
}
}
class Ticket implements Runnable {
private int tick = 100;
Object obj = new Object();
public void run() {
while (true) {
//同步代码块,将同步代码放入同步代码块中执行
synchronized (obj) {// obj为自己创建的锁旗标
if (tick > 0) {
try {
Thread.sleep(20);
}
catch (Exception e) {
}
System.out.println(Thread.currentThread().getName() + "__sale:" + tick--);
}
else {
break;
}
}
}
}
}
2.2.2 同步函数:
将多线程并发访问的代码封装到函数里面,然后在函数声明的时候加上synchronized,进行同步访问控制。
同步函数的锁是this,即本对象
package thread;
/*
* 同步函数
*/
public class TestThreadDanger {
public static void main(String[] args) {
Ticket t1 = new Ticket();
new Thread(t1, "TicketSaleMan1").start();
new Thread(t1, "TicketSaleMan2").start();
new Thread(t1, "TicketSaleMan3").start();
new Thread(t1, "TicketSaleMan4").start();
}
}
class Ticket implements Runnable {
private int tick = 100;
Object obj = new Object();
boolean haveTicket = true;
public void run() {
while (true) {
if (haveTicket)
saleTicket();
else
break;
}
}
public synchronized void saleTicket(){
if (tick > 0) {
try {Thread.sleep(20);}catch (Exception e) {}
System.out.println(Thread.currentThread().getName() + "__sale:" + tick--);
}else{
haveTicket = false;
}
}
}
2.2.3 静态同步函数
静态同步函数的锁是该类的字节码文件对象
class Ticket implements Runnable
{
private static int tick = 100;
//Object obj = new Object();
boolean flag = true;
public void run()
{
if(flag)
{
while(true)
{
synchronized(Ticket.class)
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
}
}
}
}
else
while(true)
show();
}
public static synchronized void show()
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
}
}
}
class StaticMethodDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try{Thread.sleep(10);}catch(Exception e){}
t.flag = false;
t2.start();
}
}
2.3 使用静态同步函数实现单例设计模式的懒汉式
/*
* 懒汉式,或者延迟加载,当需要的时候才创建这个类
* 懒汉式要注意的是线程安全问题
*/
class SingletonDelay{
private SingletonDelay(){};
private static SingletonDelay sin = null;
public static SingletonDelay getInstence(){
if(sin == null){//双重判断的目的是为了减少锁的比较次数,提高效率
synchronized(SingletonDelay.class){//静态方法的锁必须用类的字节码对象
if(sin==null){
sin = new SingletonDelay();
}
}
}
return sin;
}
}
2.4 死锁
死锁,当多个线程之间并发执行,出现线程无限等待的现象称为死锁。
死锁产生的可能原因:
--A线程持有锁1,A线程现在需要锁2里面被控制的资源;B线程持有锁2,B线程现在需要锁1里面被控制的资源。因此线程A与B陷入相互需要对方资源,恶性循环中。
--嵌套锁:锁里面有锁
/*
死锁。
同步中嵌套同步。
*/
class Ticket implements Runnable
{
private int tick = 1000;
Object obj = new Object();
boolean flag = true;
public void run()
{
if(flag)
{
while(true)
{
synchronized(obj)
{
show();
}
}
}
else
while(true)
show();
}
public synchronized void show()//this
{
synchronized(obj)
{
if(tick>0)
{
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
}
}
}
}
class DeadLockDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
try{Thread.sleep(10);}catch(Exception e){}
t.flag = false;
t2.start();
}
}
2.5 多线程通信
多线程通信:等待/唤醒机制.即多个线程并发访问相同资源时,分工明确的线程,一个负责生产,一个负责消费,那么在这种情况下,
资源对于它们来时就是一个需要同步访问控制的东西。资源没有了,消费者就要通知生产者生产,消费者自己等待。生产者生产好
资源了,就应该通知消费者消费,自己等待。这就是多线程通信。
2.5.1 等待唤醒机制
在jdk1.5之前等待唤醒机制是通过在同步块里面使用方法:wait() :使线程进入冻结状态,,notify():唤醒线程池中排序的第一个线程,
notifyAll()唤醒线程池中所有等待的线程。
线程通信示例:
class ProducerConsumerDemo
{
public static void main(String[] args)
{
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(pro);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
/*
对于多个生产者和消费者。
为什么要定义while判断标记。
原因:让被唤醒的线程再一次判断标记。
为什么定义notifyAll,
因为需要唤醒对方线程。
因为只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。
*/
class Resource
{
private String name;
private int count = 1;
private boolean flag = false;
// t1 t2
public synchronized void set(String name)
{
while(flag)
try{this.wait();}catch(Exception e){}//t1(放弃资格) t2(获取资格)
this.name = name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);
flag = true;
this.notifyAll();
}
// t3 t4
public synchronized void out()
{
while(!flag)
try{wait();}catch(Exception e){}//t3(放弃资格) t4(放弃资格)
System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);
flag = false;
this.notifyAll();
}
}
class Producer implements Runnable
{
private Resource res;
Producer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
res.set("+商品+");
}
}
}
class Consumer implements Runnable
{
private Resource res;
Consumer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
res.out();
}
}
}
2.5.2 JDK1.5对线程锁的升级
在JDK 1.5中,提供了改进synchronized的升级解决方案。将同步synchronized替换为显式的Lock操作,将Object中的wait,notify,notifyAll替换成Condition对象,该对象可对Lock锁进行获取。wait,notify,notifyAll这些方法都定义在Object中,是因为这些方法操作同步中的线程时,都必须表示自己所操作的线程的锁,就是说,等待和唤醒的必须是同一把锁。不可对不同锁中的线程进行唤醒。所以这就使得程序是不良的,因此,通过对锁机制的改良,使得程序得到优化。
/*
线程间通信:
等待唤醒机制:升级版
生产者消费者 多个
*/
import java.util.concurrent.locks.*;
class ProducerConsumerDemo{
public static void main(String[] args){
Resouse r = new Resouse();
Producer p = new Producer(r);
Consumer c = new Consumer(r);
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
Thread t3 = new Thread(p);
Thread t4 = new Thread(c);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Resouse{
private String name;
private int count = 1;
private boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition condition_P = lock.newCondition();
private Condition condition_C = lock.newCondition();
//要唤醒全部,否则都可能处于冻结状态,那么程序就会停止。这和死锁有区别的。
public void set(String name)throws InterruptedException{
lock.lock();
try{
while(flag)//循环判断,防止都冻结状态
condition_P.await();
this.name = name + "--" + count++;
System.out.println(Thread.currentThread().getName() + "..生成者--" + this.name);
flag = true;
condition_C.signal();
}finally{
lock.unlock();//释放锁的机制一定要执行
}
}
public void out()throws InterruptedException{
lock.lock();
try{
while(!flag)//循环判断,防止都冻结状态
condition_C.await();
System.out.println(Thread.currentThread().getName() + "..消费者." + this.name);
flag = false;
condition_P.signal();//唤醒全部
}finally{
lock.unlock();
}
}
}
class Producer implements Runnable{
private Resouse r;
Producer(Resouse r){
this.r = r;
}
public void run(){
while(true){
try{
r.set("--商品--");
}catch (InterruptedException e){}
}
}
}
class Consumer implements Runnable{
private Resouse r;
Consumer(Resouse r){
this.r = r;
}
public void run(){
while(true){
try{
r.out();
}catch (InterruptedException e){}
}
}
}
2.6 停止线程
从JDK1.5自后线程里面的方法stop()显示已经过时,那么我们怎么停止线程呢?肯定是结束run()方法呀,怎么结束run()方法呢?一般情况下,run方法里面就有while循环,我们结束循环就可以了。
通过标记结束循环:
class StopThread implements Runnable{
private boolean flag = true;
public synchronized void run(){
while (flag){
try{
wait();
}catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "----Exception");
flag = false;
}
System.out.println(Thread.currentThread().getName() + "----run");
}
}
public void changeFlag(){
flag = false;
}
}
class StopThreadDemo{
public static void main(String[] args) {
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
int n = 0;
while (true){
if (n++ == 60){
st.changeFlag();
break;
}
System.out.println("Hello World!");
}
}
}
上面有特殊情况:当线程处于冻结状态,就不会读取标记,那么线程就不会结束
因此我们必须使用Thread类里面提供的interrupt()方法,来中断线程,但不会让它死掉,会让线程脱了冻结状态,恢复到运行状态,再去判断标记,然后结束循环,最终跳出run方法。
class StopThread implements Runnable{
private boolean flag = true;
public synchronized void run(){
while (flag){
try{
wait();
}catch (InterruptedException e){
System.out.println(Thread.currentThread().getName() + "----Exception");
flag = false;
}
System.out.println(Thread.currentThread().getName() + "----run");
}
}
}
class StopThreadDemo{
public static void main(String[] args){
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
int n = 0;
while (true){
if (n++ == 60){
t1.interrupt();
t2.interrupt();
break;
}
System.out.println("Hello World!");
}
}
}
2.7 守护线程
在线程创建后,在启动以前,调用方法setDeamon(),可以将一个线程标记为守护线程。如果这个线程结束,则java虚拟机也会结束。
2.8 join线程
当A线程执行到B线程方法时,A线程就会等待,B线程都执行完,A才会执行。join可用来临时加入线程执行。
class Demo implements Runnable{
public void run(){
for(int x=0;x<90;x++){
System.out.println(Thread.currentThread().getName() + "----run" + x);
}
}
}
class JoinDemo{
public static void main(String[] args)throws Exception{
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t2.start();
t1.join();//等t1执行完了,主线程才从冻结状态恢复,和t2抢执行权。t2执不执行完都无所谓。
int n = 0;
for(int x=0;x<80;x++){
System.out.println(Thread.currentThread().getName() + "----main" + x);
}
System.out.println("Over");
}
}
2.9 其它方法:
设置线程优先级:可通过setPriority()设定,如:setPriority(Thread.MAX_PRIORITY)设优先级为最大。
不执行这个线程,跳到其它线程:yield() 通过这个方法,可稍微减少线程执行频率,达到线程都有机会平均被执行的效果。
相关文章
- 暂无相关文章
用户点评