Thread实现多线程、死锁、同步锁
1、实现一个类MyThread继承Thread并重写run()方法
启动线程方法:实例化MyThread对象,并调用start()方法
多个线程之间交替执行(抢时间片)
主线程(main方法)有优先运行的权限,但并不绝对
2、实现一个类MyThread实现Runnable接口下的run()方法
启动线程方法:实例化MyThread对象mt,并将mt作为实例化Thread的参数,再通过Thread对象调用start()方法
MyThread mt = new MyThread();
Thread t = new Thread(mt);
t.start();
实现点击按钮,球向前慢慢滚动的效果:
public class Test extends JFrame{
private MyPanel mp;
public static void main(String[] args) {
new Test().setVisible(true);
}
private JPanel panel;
private JButton btn;
public Test() {
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
panel = new JPanel();
panel.setLayout(new BorderLayout());
setBounds(300, 100, 600, 300);
this.setContentPane(panel);
mp = new MyPanel();
panel.add(mp);
btn = new JButton("开始");
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Thread t = new Thread(mp);
t.start();
}
});
panel.add(btn,BorderLayout.SOUTH);
}
private class MyPanel extends JPanel implements Runnable{
private int x = 100;
public void paint(Graphics g) {
super.paint(g);
g.fillOval(x, 80, 50, 50);
}
@Override
public void run() {
for (int i = 0; i < 50; i++) {
x += 5 ;
try {
Thread.sleep(150);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
repaint();
}
}
}
}
3、使用匿名类,继承Thread,重写run()方法,直接在run()方法中写业务代码
匿名类的一个好处是可以很方便的访问外部的局部变量
public class TestThread {
public static void main(String[] args) {
Hero gareen = new Hero();
gareen.name = "盖伦";
gareen.hp = 616;
gareen.damage = 50;
Hero teemo = new Hero();
teemo.name = "提莫";
teemo.hp = 300;
teemo.damage = 30;
Hero bh = new Hero();
bh.name = "赏金猎人";
bh.hp = 500;
bh.damage = 65;
Hero leesin = new Hero();
leesin.name = "盲僧";
leesin.hp = 455;
leesin.damage = 80;
//匿名类
Thread t1= new Thread(){
public void run(){
//匿名类中用到外部的局部变量teemo,必须把teemo声明为final
//但是在JDK7以后,就不是必须加final的了
while(!teemo.isDead()){
gareen.attackHero(teemo);
}
}
};
t1.start();
Thread t2= new Thread(){
public void run(){
while(!leesin.isDead()){
bh.attackHero(leesin);
}
}
};
t2.start();
}
}
注意: 启动线程是start()方法,run()并不能启动一个新的线程
常见线程方法:
sleep 当前线程暂停
Thread.sleep(1000); 表示当前线程暂停1000毫秒 ,其他线程不受影响
join 加入到当前线程中
Thread t1= new Thread(){
public void run(){
while(!teemo.isDead()){
gareen.attackHero(teemo);
}
}
};
t1.start();
try {
//main线程在运行时,t1线程加入到main线程中来,只有t1线程运行结束,才会继续往下走
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
setPriority 线程优先级
当线程处于竞争关系的时候,优先级高的线程会有更大的几率获得CPU资源
Thread t1 = new Thread();
Thread t2 = new Thread();
t1.setPriority(Thread.MAX_PRIORITY);最大为10
t2.setPriority(Thread.MIN_PRIORITY);最小为0
yield 临时暂停
当前线程,临时暂停,使得其他线程可以有更多的机会占用CPU资源
setDaemon 守护线程
如果一个进程只剩下守护线程,那么进程就会自动结束。
线程同步
没有实现同步
public class Bank {
public static void main(String[] args) {
new Bank().way();
}
public void way() {
Family f = new Family();
Thread t1 = new Thread(f, "丈夫");
//new Thread(f, "线程名称");
Thread t2 = new Thread(f, "妻子");
t1.start();
t2.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
while (true) {
if (f.times >= 2) {
f.show();
break;
}
}
}
private class Family implements Runnable {
private int totalMoney;
private int getMoney;
private int currentMoney;
private int times = 0;
private Object key = new Object();
public Family() {
totalMoney = 5000;
getMoney = 2000;
currentMoney = 0;
}
@Override
public void run() {
//Thread.currentThread()当前线程
System.out.println(Thread.currentThread().getName() + "---取了" + getMoney + "元");
//给取出的钱重新赋值
currentMoney += getMoney;
//银行存款剩余
int tem = totalMoney - getMoney;
//在银行存款变换之前,线程沉睡1s,这时,时间片被另一个线程抢走
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//给银行存款重新赋值
totalMoney = tem;
times++;
}
public void show() {
System.out.println("银行存款剩余:" + totalMoney + "\t取出:" + currentMoney);
}
}
}
运行:
丈夫---取了2000元
妻子---取了2000元
银行存款剩余:3000 取出:4000
添加同步锁:
方法一:
//对象必须在run()方法体外面
private Object key = new Object();
public void run() {
//synchronized( 可以是任意对象,例如:this)
synchronized (key) {
System.out.println(Thread.currentThread().getName() + "---取了" + getMoney + "元");
currentMoney += getMoney;
int tem = totalMoney - getMoney;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
totalMoney = tem;
times++;
}
}
方法二:
public void run() {
syn();
}
public synchronized void syn() {
System.out.println(Thread.currentThread().getName() + "---取了" + getMoney + "元");
currentMoney += getMoney;
int tem = totalMoney - getMoney;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
totalMoney = tem;
times++;
}
运行:
丈夫---取了2000元
妻子---取了2000元
银行存款剩余:1000 取出:4000
死锁
1. 线程1 首先占有对象1,接着试图占有对象2
2. 线程2 首先占有对象2,接着试图占有对象1
3. 线程1 等待线程2释放对象2
4. 与此同时,线程2等待线程1释放对象1
就会。。。一直等待下去,直到天荒地老,海枯石烂,山无棱 ,天地合。。。
public class Test04 {
public static void main(String[] args) {
new Test04();
}
public Test04() {
MyThread mt = new MyThread();
Thread t1 = new Thread(mt,"A");
Thread t2 = new Thread(mt,"B");
t1.start();t2.start();
}
public class MyThread implements Runnable{
private Object o1 = new Object();
private Object o2 = new Object();
private boolean b = false;
@Override
public void run() {
if(b) {
b = !b;
synchronized (o1) {
System.out.println(Thread.currentThread().getName()+"拿到第一把钥匙!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (o2) {
System.out.println(Thread.currentThread().getName()+"拿到第二把钥匙!");
}
}
}else {
b = !b;
synchronized (o2) {
System.out.println(Thread.currentThread().getName()+"拿到第二把钥匙!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (o1) {
System.out.println(Thread.currentThread().getName()+"拿到第一把钥匙!");
}
}
}
}
}
}
运行:
B拿到第一把钥匙!
A拿到第二把钥匙!
(一直在运行中,程序没有结束!)