实现线程的两种方式:Thread类 和 实现Runnable接口
注意:Thread是实现了Runnable接口的
windows系统中,系统会为每个线程分配CPU时间片
使线程处于就绪状态:
- 调用sleep方法
- 调用wait()方法,线程进入等待状态
- 等待输入输出完成
线程再次进入运行状态
- notify()方法
- nofityAll()方法
- interrupt()方法
- 线程休眠时间结束
- 输入输出结束
线程操作
休眠
中断(不要使用stop()方法),写死循环用用break退出循环
礼让 yield() 告知下一个线程礼让给其他线程
这里写代码片
package jerryzaki3;
//两个线程同时运行的案例 主要考量有两个线程同时运行的情况
/*
* 挡实现runnable接口时,要会调通启动方法start()
* */
public class Threaddemo2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Pig pig=new Pig(10);
Person p=new Person(10);
Thread t1=new Thread(pig);
Thread t2=new Thread(p);
t1.start();
t2.start();
}
}
class Pig implements Runnable{
int n;
int count=0;
public Pig(int n) {
this.n=n;
}
public void run() {
while(true) {
try {
//调用sleep要捕获异常
Thread.sleep(1000);
} catch (Exception e) {
// TODO: handle exception
}
count++;
System.out.println("我是线程1 "+count+"只猪");
if(count==n) {
//跳出死循环
break;
}
}
}
}
//
class Person implements Runnable{
int n=0;
int res=0;
int count=0;
public Person(int n) {
this.n=n;
}
public void run() {
while(true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
res+=(count++);
System.out.println("我是线程2"+"计算结果"+res);
if(count==n) {
System.out.println("--------------------");
System.out.println("最后结果"+res);
break;
}
}
}
}
线程同步
用同步代码块
package jerryzaki3;
/*
* 航空公司出票系统 一共2000张票
* 由3个售票窗口出售 编写程序
*
*
* 并发引起的不安全如何解决(对象锁)
* 对象锁标志位:0 1 j 对象都有一个对象都有标志位(object)
*默认是1 标识没有被使用
*当前一个线程还在执行,下一个线程会进入等待池
*当对象锁标识位一直为0时,就产生死锁
*
*synchronized(object) 任意一个对象都可以加锁
* */
public class Threaddemo4 {
public static void main(String[] args) {
// TODO Auto-generated method stub
TicketWindow tw=new TicketWindow();
Thread t1=new Thread(tw);
Thread t2=new Thread(tw);
Thread t3=new Thread(tw);
t1.start();
t2.start();
t3.start();
}
}
//售票窗口类
class TicketWindow implements Runnable{
//一共两千张票
private int nums=2000;
public void run() {
while(true) {
//认为下面这段代码要保证原子性(代码同步性)
synchronized (this) {
if(nums>0) {
//显示售票信息
//当前线程的名字
System.out.println
(Thread.currentThread().getName()+"正在出售"+nums);
//出票速度
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
nums--;
}else {
//售票结束
break;
}
}
}
}
}
使用同步方法
在run()方法里去调用同步方法
public sysnchronized void doit(){
//code
}
public void run(){
while(true){
doit();
}
}
wait nofity
notify() 和notifyAll()
顾名思义notify()是唤醒一个正在等待的线程,notifyAll()是唤醒所有正在等待的线程。这样的解释 并不会让我们很好理解
二者之间的区别。
notify()
notify是通知操作系统唤醒一个正在等待的获取对象锁的线程,当有多个等待线程时候, 操作系统会根据一定的调度算法调
度一个线程,当正在占有对象锁的线程释放锁的时候操作系统调度的这个线程就会执行。而而剩下的那些没有被notify的线程
就不会获取执行机会。
notifyAll()
当有多个等待线程时,所有的等待线程都会被唤醒,他们会根据操作系统的调度顺序依次执行。下面的代码说明二者的区别:
public class NofityTest implements Runnable {
private Object lock ;
private int num ;
public NofityTest(Object lock, int i) {
this.lock = lock ;
num = i ;
}
@Override
public void run() {
synchronized(lock){
try {
lock.wait();
System.out.println("--- "+num);
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// 利用obj 的wait 方法实
final Object lock = new Object() ;
new Thread(new NofityTest(lock,1)).start();
new Thread(new NofityTest(lock,2)).start();
try {
// 等待另外两个线程进入wait状态
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock){
// 请将 lock.notify() 改成lock.notifyAll() 再执行观察二者区别 !!
lock.notify();
}
}
}
wait()和notify()
对象的wait()是让当前线程释放该对象Monitor锁并且进入访问该对象的等待队列,当前线程会进入挂起状态,等待操作系统唤起(notify)
挂起的线程重新获取对该对象的访问锁才能进入运行状态。因为自身已经挂起,所以已经挂起的线程无法唤醒自己,必须通过别的线程
告诉操作系统,再由操作系统唤醒。Monitor是不能被并发访问的(否则Monitor状态会出错,操作系统根据错误的状态调度导致系统错乱),
而wait和nofity 正是改变Monitor的状态(请参考 PV操作) 所以使用wait、notify方法时,必须对对象使用synchronized加锁,只有线程获
取对象的Monitor锁之后才能进行wait、notify操作否则将抛出IllegalMonitorStateException异常。我们来看一段代码:
public class PrintAB implements Runnable{
private Object lock ;
private char ch ;
public PrintAB(Object lock ,char ch){
this.lock = lock ;
this.ch = ch ;
}
@Override
public void run() {
while(true){
synchronized (lock){
try {
/**
* 第一次执行并不会唤醒任何线程,
* 第二次以及以后就会唤醒另外一个线程获取Monitor锁,因为只有一个线程挂起
* 而notify() 就是唤醒一个线程
*/
lock.notify();
System.out.println(Thread.currentThread().getName()+":"+ch);
/**
* synchronized 代码块执行完毕之后才会交出Monitor锁,别的线程才有执行机会
* wait 执行过之后当前线程就挂起了,然后释放锁,接着已经被操作系统notify
* 的线程获取Monitor 开始执行
*/
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
// 我们最好将 lock 声明为final ,防止重新赋值后导致synchronized锁定对象发生改变。
final Object lock = new Object();
new Thread(new PrintAB(lock,'A')).start();
new Thread(new PrintAB(lock,'B')).start();
}
}
上述代码实现了交替打印 字符A 和字符B 。当一个线程打印完毕之后自己就会挂起,必须等待另外一个线程打印并将
之唤醒,就实现了交替打印的效果。