一、 线程创建方式
线程创建有二种方式,继承Thread、或者实现Runnable接口。
继承Thread。 new Thread();
public class MyThread extends Thread{
public MyThread(String name) {
super(name);
}
@Override
public void run() {
super.run();
System.out.println("线程实现方式一:继承Java.lang.Thread");
System.out.println("线程名: "+this.getName());
}
}
实现Runnable接口。Thread(new Runnable(){...});
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("创建线程的第二种方式,实现Runnable接口");
}
}
二、线程状态-生命周期
线程的状态(生命周期)大概可以按照下图表示。
三 、多线程和锁对象
1 这里表达的多线程通常是指2个或者以上线程同时访问同一资源现象。
例如:两个线程ThreadA、ThreadB都操作同一个对象Foo对象,并修改Foo对象上的数据。
package cn.thread;
public class Foo {
private int x = 100;
public int getX() {
return x;
}
public int fix(int y) {
x = x - y;
return x;
}
}
package cn.thread;
public class MyRunnable implements Runnable {
private Foo foo = new Foo();
public static void main(String[] args) {
MyRunnable run = new MyRunnable();
Thread ta = new Thread(run, "Thread-A");
Thread tb = new Thread(run, "Thread-B");
ta.start();
tb.start();
}
public void run() {
for (int i = 0; i < 3; i++) {
this.fix(30);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " : 当前foo对象的x值= " + foo.getX());
}
}
public int fix(int y) {
return foo.fix(y);
}
}
Thread-B : 当前foo对象的x值= 40
Thread-A : 当前foo对象的x值= 40
Thread-B : 当前foo对象的x值= -20
Thread-A : 当前foo对象的x值= -20
Thread-B : 当前foo对象的x值= -80
Thread-A : 当前foo对象的x值= -80
从结果发现,这样的输出值明显是不合理的。原因是两个线程不加控制的访问Foo对象并修改其数据所致。如果要运行结果合理,我们需要通过同步(同步函数或者同步代码块)加锁。
package cn.thread;
public class Foo2 {
private int x = 100;
public int getX() {
return x;
}
//同步方法
public synchronized int fix(int y) {
x = x - y;
System.out.println("线程"+Thread.currentThread().getName() + "运行结束,减少“" + y
+ "”,当前值为:" + x);
return x;
}
// //同步代码块
// public int fix(int y) {
// synchronized (this) {
// x = x - y;
// System.out.println("线程"+Thread.currentThread().getName() + "运行结束,减少“" + y
// + "”,当前值为:" + x);
// }
//
// return x;
// }
}
package cn.thread;
public class MyRunnable2 {
public static void main(String[] args) {
MyRunnable2 run = new MyRunnable2();
Foo2 foo2=new Foo2();
MyThread t1 = run.new MyThread("线程A", foo2, 10);
MyThread t2 = run.new MyThread("线程B", foo2, -2);
MyThread t3 = run.new MyThread("线程C", foo2, -3);
MyThread t4 = run.new MyThread("线程D", foo2, 5);
t1.start();
t2.start();
t3.start();
t4.start();
}
class MyThread extends Thread {
private Foo2 foo2;
/**当前值*/
private int y = 0;
MyThread(String name, Foo2 foo2, int y) {
super(name);
this.foo2 = foo2;
this.y = y;
}
public void run() {
foo2.fix(y);
}
}
}
线程线程A运行结束,减少“10”,当前值为:90
线程线程C运行结束,减少“-3”,当前值为:93
线程线程B运行结束,减少“-2”,当前值为:95
线程线程D运行结束,减少“5”,当前值为:90
2 锁细节
1、每个java对象都有一个锁对象.而且只有一把钥匙.某线程拿到对象之后,其他线程处于阻塞状态,进入锁对象的线程程等待唤醒!
2、只有当对象具有同步方法或者代码块时,内置锁才会起作用.
3、sleep进入睡眠状态,该线程会继续持有锁,不会释放.
4、非静态同步函数的锁默认为this, 静态同步函数的锁则是this.getClass().
四 、代码块和锁设计
1 同步代码块设计
设计同步函数或者代码块时,应该考虑共享资源所在位置,尽可能缩小代码块的范围,同步会降低运行效率。
2 锁设计
如果一个对象是多个线程的共享资源,应该尽可能在线程类中用属性维护起来,同时考虑在该对象对应的类中使用同步(函数或者代码块)加锁。