public class EggTimer {
private final Timer timer = new Timer();
private final int minutes;
public EggTimer(int minutes) {
this.minutes = minutes;
}
public void start() {
timer.schedule(new TimerTask() {
public void run() {
playSound();
// timer.cancel();
}
private void playSound() {
System.out.println("Your egg is ready!");
// Start a new thread to play a sound...
}
}, 0,minutes * 60 * 1000);//几分钟执行一次
}
public static void main(String[] args) {
EggTimer eggTimer = new EggTimer(2);
eggTimer.start();
}
}
同步代码块
为了保证线程能正常的执行原子操作,java引入了同步机制,在代码原子操作的程序代码前加上synchronized标记 ,这样,任何时刻只允许一个线程拥有这把锁,
这样当加了同步标记后,其他待进入线程放到Stack对象锁池中,线程进入阻塞状态。加锁方式如下:
Public synchronized String pop(){…}
也可以写作
Public String pop(){
Synchronized(this){…}
}
下面看一个消费者,创造者的例子
public class SyncTest {
public static void main(String[] args) {
Stack stack =new Stack("stack");
Producer producer1=new Producer(stack,"producer1");
Consumer consumer1=new Consumer(stack,"consumer");
}
}
class Producer extends Thread{
private Stack theStack;
public Producer(Stacks,String name){
super(name);
theStack = s;
start();
}
public void run(){
String goods;
for(int i=0;i<20;i++){
goods="goods"+(theStack.getPoint()+1);
theStack.push(goods);
System.out.println(getName()+": push "+goods+" to "+theStack.getName());
yield();
}
}
}
class Consumer extends Thread{
private Stack theStack;
public Consumer(Stacks,String name)
{
super(name);
theStack=s;
start();
}
public void run(){
String goods;
for(int i =0;i<20;i++){
goods=theStack.pop();
System.out.println(getName()+":pop "+goods+" from "+theStack.getName());
yield();
}
}
}
class Stack{
private String name;
private String[] buffer=new String[100];
int point = -1;
public Stack(String name){this.name=name;}
public String getName(){return name;}
public int getPoint(){return point;}
public synchronized String pop(){
String goods=buffer[point];
buffer[point]=null;
Thread.yield();
point --;
return goods;
}
public synchronized void push(String goods){
point ++;
Thread.yield();
buffer[point] =goods;
}
}
同步和并发问题
同步锁解决资源共享资源竞争的有效手段,当一个线程已经操作共享资源,其他线程只有等待,只有当已经在操作共享资源的线程执行完毕后才能同步代码块,其他线程才能有机会共享。
以打水为例子,10个去打水,每人要打10桶,使用同步方式为,一个人打完10桶后,其他人才有机会打水,,当一个人打水,其他人必须等待,显然不合理。
public class Person extends Thread{
private Well well;
public Person (Well well){
this.well=well;
start();
}
public void run(){
synchronized(well){
for(int i=0;i<10;i++){
well.withdraw();
yield();
}
}
}
/**
* @param args
*/
public static void main(String[] args) {
Well well=new Well();
Person persons[]=new Person[10];
for(int i=0;i<10;i++){
persons[i]=new Person(well);
}
}
}
class Well{
private int water=100;
public void withdraw(){
water --;
System.out.println(Thread.currentThread().getName()+": water left:"+water);
}
}
则我们把同步锁加在withdraw(),则可以解决问题,例:
public class Person extends Thread{
private Well well;
public Person (Well well){
this.well=well;
start();
}
public void run(){
for(int i=0;i<10;i++){
well.withdraw();
yield();
}
}
public static void main(String[] args) {
Well well=new Well();
Person persons[]=new Person[10];
for(int i=0;i<10;i++){
persons[i]=new Person(well);
}
}
}
class Well{
private int water=100;
public synchronized void withdraw(){
water --;
System.out.println(Thread.currentThread().getName()+": water left:"+water);
}
}
线程安全类
一个线程安全类必须满足以下条件:
1、 这个类可以同时被多个线程安全访问。
2、 每个线程都能正常执行原子操作,得到正确结果。
3、 原子操作万仇,对象处于逻辑是合理状态。
如上我们使用的Stack类就是可变类,point和buffer都可以发生变化,会造成共享资源的竞争。
线程之间的通信
不同线程之间执行不同的任务,如果任务有某种联系,必须实现通信功能,协调完成工作。
Java.lang.object类提供了用于线程通信的方法:
1、 wait(),该方式的线程释放对象锁,虚拟机把该对象放到等待池中,等待其他线程唤醒。
2、 notify(),该方法唤醒在等待池中的线程,虚拟机从等待池中随机选择一个线程.
3、 notifyAll()方法用于把对象池中所有的线程都转到对象锁池中。
例t1线程和t2线程可用wait和notify进行通信。
以编程的方式控制线程
实际编程中,一般是在受控的线程中定义一个标志量,其他线程通过改变标准变量的值,来控制线程的暂停、恢复运行已经自然终止,jdk1.2开始一些废弃了一些停止线程的方法
suspend() 使线程暂停;
resume() 暂停的线程恢复运行;
stop() 终止线程;
废弃的原因大概由于以上几个方法容易导致死锁等。
ThreadLocal类
java.lang.ThreaLocal一般用来存放线程的局部变量,每个线程一般都有单独的局部变量,彼此 之间不会共享,该类主要有三个方法:
1、 public T get() 返回巨变线程的变量。
2、 protected T initialValue() 返回局部变量的初始值;
3、 public void set(T value) 设置当前线程的局部变量;
其实看看TheadLocal的源码,我们可以发现他的思路,在类中有一个Map缓存,用于存储每个线程的局部变量,例:
package com.bin.duoxiancheng; import java.util.Collections; import java.util.HashMap; import java.util.Map; public class ThradLocal{ private Map values= Collections.synchronizedMap(new HashMap ()); public T get(){ Thread cuThread = Thread.currentThread(); T o=values.get(cuThread); if(o ==null && !values.containsKey(cuThread)){ o=initValue(); values.put(cuThread, o); } return o; } public void set(T newValue){ values.put(Thread.currentThread(), newValue); } protected T initValue(){ return null; } }
以上程序仅仅实现了总体思路,细节上ThreadLocal类实现的更健壮,还保证了线程结束后,从Map缓存中删除这个线程的局部变量的引用。