Java笔记(二)
10.
public | protected | default | private | |
同一个类中 | √ | √ | √ | √ |
同一个包中 | √ | √ | √ | |
子类 | √ | √ | ||
不同包中 | √ |
11. 线程:
static Thread currentThread():获取当前线程对象
getName():获取线程名称
设置线程名称:setName()或者构造函数
创建线程的方式:
1. 继承Thread类
(1)定义类,继承Thread
(2)复写Thread类中的void run()方法(因为Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。)
(3)调用线程的start方法,该方法能启动线程,并能调用run方法
注:对象.run()仅仅是对象调用方法。而线程创建了,并没有运行;
对象.start()开启线程并执行该线程的run方法
2. 实现Runnable接口
(1)定义类,实现Runnable接口
(2)覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中
(3)通过Thread类建立线程对象
(4)将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。因为,自定义的run方法所属的对象是Runnable接口的子类对象,所以要让线程去执行指定对象的run方法,就必须明确该run方法所属的对象。
(5)调用Thread类的start方法开启线程,并调用Runnable接口子类的run方法
实现方式与继承方式的区别:
实现方式好处:避免了单继承的局限性。在定义线程时,建议使用实现方式。
继承Thread:线程代码存放在Thread子类run方法中;实现Runnable:线程代码存放在接口的子类的run方法。
同步函数的锁是this;静态同步函数的锁是Class对象(类名.class)
死锁:例:
1 class DeadLock implements Runnable{ 2 private boolean flag; 3 DeadLock(boolean flag){ 4 this.flag = flag; 5 } 6 public void run(){ 7 if(flag){ 8 while(true){ 9 synchronized(Lock.locka){ 10 System.out.println("if locka"); 11 synchronized(Lock.lockb){ 12 System.out.println("if lockb"); 13 } 14 } 15 } 16 } 17 else{ 18 while(true){ 19 synchronized(Lock.lockb){ 20 System.out.println("else lockb"); 21 synchronized(Lock.locka){ 22 System.out.println("else locka"); 23 } 24 } 25 } 26 } 27 } 28 } 29 30 class Lock{ 31 public static Object locka = new Object(); 32 public static Object lockb = new Object(); 33 } 34 35 public class Demo{ 36 public static void main(String[] args) { 37 Thread t1 = new Thread(new DeadLock(true)); 38 Thread t2 = new Thread(new DeadLock(false)); 39 t1.start(); 40 t2.start(); 41 } 42 }
wait() notify() notifyAll():
都使用在同步中,因为要对持有监视器(锁)的线程操作,只有同步才具有锁。
这些方法在操作同步中线程时,都必须要标识它们所操作线程持有的锁。只有同一个锁上的被等待线程,可以被同一个锁上的notify()唤醒,不可以对不同锁中的线程进行唤醒。也就是说,等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被对象调用的方法定义Object类中。
生产者-消费者问题:
1 class Resource{ 2 private String name; 3 private int count = 1; 4 private boolean flag = false; 5 6 public synchronized void set(String name){ 7 while(flag){ 8 try { 9 this.wait(); 10 } catch (InterruptedException e) { 11 e.printStackTrace(); 12 } 13 } 14 this.name = name + " ~~ " + count++; 15 System.out.println(Thread.currentThread().getName() + "..生产者.." + this.name); 16 flag = true; 17 this.notifyAll(); 18 } 19 20 public synchronized void out(){ 21 while(!flag){ 22 try { 23 this.wait(); 24 } catch (InterruptedException e) { 25 e.printStackTrace(); 26 } 27 } 28 System.out.println(Thread.currentThread().getName() + "....消费者...." + this.name); 29 flag = false; 30 this.notifyAll(); 31 } 32 } 33 34 class Producer implements Runnable{ 35 private Resource r; 36 Producer(Resource r){ 37 this.r = r; 38 } 39 public void run(){ 40 while(true){ 41 r.set("商品"); 42 } 43 } 44 } 45 46 class Consumer implements Runnable{ 47 private Resource r; 48 Consumer(Resource r){ 49 this.r = r; 50 } 51 public void run(){ 52 while(true){ 53 r.out(); 54 } 55 } 56 } 57 58 public class Demo{ 59 public static void main(String[] args) { 60 Resource r = new Resource(); 61 Thread t1 = new Thread(new Producer(r)); 62 Thread t2 = new Thread(new Producer(r)); 63 Thread t3 = new Thread(new Consumer(r)); 64 Thread t4 = new Thread(new Consumer(r)); 65 t1.start(); 66 t2.start(); 67 t3.start(); 68 t4.start(); 69 } 70 }
对于多个生产者和消费者。定义while判断标记的原因:让被唤醒的线程再一次判断标记。
定义notifyAll的原因:需要唤醒对方线程。如果只用notify,容易 出现只唤醒本方线程的情况。导致程序中的所有线程都等待。
生产者-消费者(升级版):
1 import java.util.concurrent.locks.Condition; 2 import java.util.concurrent.locks.Lock; 3 import java.util.concurrent.locks.ReentrantLock; 4 5 class Resource{ 6 private String name; 7 private int count = 1; 8 private boolean flag = false; 9 private Lock lock = new ReentrantLock(); 10 private Condition condition_pro = lock.newCondition(); 11 private Condition condition_con = lock.newCondition(); 12 13 public void set(String name) throws InterruptedException{ 14 lock.lock(); 15 try{ 16 while(flag){ 17 condition_pro.await(); 18 } 19 this.name = name + " ~~ " + count++; 20 System.out.println(Thread.currentThread().getName() + "..生产者.." + this.name); 21 flag = true; 22 condition_con.signal(); 23 }finally{ 24 lock.unlock(); 25 } 26 } 27 28 public void out() throws InterruptedException{ 29 lock.lock(); 30 try{ 31 while(!flag){ 32 condition_con.await(); 33 } 34 System.out.println(Thread.currentThread().getName() + "....消费者...." + this.name); 35 flag = false; 36 condition_pro.signal(); 37 }finally{ 38 lock.unlock(); 39 } 40 } 41 } 42 43 class Producer implements Runnable{ 44 private Resource r; 45 Producer(Resource r){ 46 this.r = r; 47 } 48 public void run(){ 49 while(true){ 50 try { 51 r.set("商品"); 52 } catch (InterruptedException e) { 53 e.printStackTrace(); 54 } 55 } 56 } 57 } 58 59 class Consumer implements Runnable{ 60 private Resource r; 61 Consumer(Resource r){ 62 this.r = r; 63 } 64 public void run(){ 65 while(true){ 66 try { 67 r.out(); 68 } catch (InterruptedException e) { 69 e.printStackTrace(); 70 } 71 } 72 } 73 } 74 75 public class Demo{ 76 public static void main(String[] args) { 77 Resource r = new Resource(); 78 Thread t1 = new Thread(new Producer(r)); 79 Thread t2 = new Thread(new Producer(r)); 80 Thread t3 = new Thread(new Consumer(r)); 81 Thread t4 = new Thread(new Consumer(r)); 82 t1.start(); 83 t2.start(); 84 t3.start(); 85 t4.start(); 86 } 87 }
JDK1.5中提供了多线程升级解决方案。将同步Synchronized替换成Lock操作。将Object中的wait、notify、notifyAll替换成了Condition对象,该对象可以Lock锁进行获取。
如何停止线程:stop方法已经过时,只能让run方法结束。开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:当线程处于冻结状态。就不会读取到标记,那么线程就不会结束。当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除。强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。
Thread类提供该方法interrupt();
1 class StopThread implements Runnable{ 2 private boolean flag = true; 3 public synchronized void run(){ 4 while(flag){ 5 try{ 6 wait(); 7 }catch(InterruptedException e){ 8 System.out.println(Thread.currentThread().getName() + "...exception"); 9 } 10 System.out.println(Thread.currentThread().getName() + "...run"); 11 } 12 } 13 public void changeFlag(){ 14 flag = false; 15 } 16 } 17 18 public class Demo{ 19 public static void main(String[] args) { 20 StopThread st = new StopThread(); 21 Thread t1 = new Thread(st); 22 Thread t2 = new Thread(st); 23 t1.start(); 24 t2.start(); 25 26 int num = 0; 27 while(true){ 28 if(num++ == 60){ 29 t1.interrupt(); 30 t2.interrupt(); 31 st.changeFlag(); 32 break; 33 } 34 System.out.println(Thread.currentThread().getName() + "....." + num); 35 } 36 System.out.println("over"); 37 } 38 }
输出结果:
... ... main.....59 main.....60 over Thread-0...exception Thread-0...run Thread-1...exception Thread-1...run
守护线程:即后台线程。线程对象.setDaemon(true)
当正在运行的线程都是守护线程时(即前台线程全结束),JVM退出。该方法必须在启动线程前调用。
Join方法:线程对象.join(),当A线程执行到了B线程的.join()方法,A就会等待。等B线程都执行完,A才会执行。join可以用来临时加入线程执行。
yield方法:暂停当前正在执行的线程对象,并执行其他线程。