Runnable和Thread的区别原文
线程锁原文
鉴于我这篇文章被鄙视了,哈哈哈哈。我决定整理一下资源共享线程同步相关的知识。欢迎鄙视并谈一谈见解。
Java传统多线程的实现有两种方法,继承Thread类或者实现Runnable
在这之前需要让大家从源码上了解一下Thread和runnable这两个类,Thread 也是实现自Runnable,在runnable接口里面有个run方法。
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
那其实这个
run其实就是为了多线程方面的一些扩展抽象出来的一个方法。
那我们就来看看Thread 对这个方法的实现做了什么。
@Override
public void run() {
if (target != null) {
target.run();
}
}
内容很简短明了。大概就是执行target的run方法,这个target也有一个run方法。这个target是什么?
全局搜索看一下,在声明中可以看到。
/* What will be run. */
private Runnable target;
说明这个target是一个runnable对象。而它正是从Thread的构造里面传进来的。
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
到这里大家可能就会明白为什么多个Thread对象传入同一个Runnable对象可以实现资源共享了。
因为runnable对象是同一个啊。
我们都知道线程启动是调用了Thread对象中的start方法;
可以看一下start方法到底做了什么
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
这里面 的group是一个线程组对象ThreadGroup。start0()是一个原生方法,大概是调用jvm里面的方法了。started则是一个标志位。
说明它会把这个线程加到线程组中,这个线程组就是用来保存当前线程的一个容器,可以监测线程的生老病死,还有设置组内的优先级等,线程的执行则由start0这个原生的方法控制。
实现Runnable接口相比继承Thread类有如下好处:
1.避免单继承的局限,一个类可以同时实现多个接口
2.适合资源的共享.
接下来就写几个案例来说明资源共享和线程同步
class MyThread extends Thread {
private int ticket = 5;
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
sale();
}
}
//使用同步方法
public void sale() {
if (this.ticket > 0) {
System.out.println(Thread.currentThread().getName()
+ "卖票:1张 " + this.ticket--);
}
}
public static void main(String[] args) {
// MyThread mt = new MyThread();
Thread thread1 = new MyThread("售票口一");
Thread thread2 = new MyThread("售票口二");
Thread thread3 = new MyThread("售票口三");
thread1.start();
thread2.start();
thread3.start();
}
}
执行结果如下:
售票口一卖票: 5
售票口一卖票: 4
售票口一卖票: 3
售票口一卖票: 2
售票口一卖票: 1
售票口三卖票: 5
售票口三卖票: 4
售票口三卖票: 3
售票口三卖票: 2
售票口三卖票: 1
售票口二卖票: 5
售票口二卖票: 4
售票口二卖票: 3
售票口二卖票: 2
售票口二卖票: 1
这边可以看出每个线程都有5张票。并没有资源共享。
class MyThread implements Runnable {
private int ticket = 5;
@Override
public void run() {
for (int i = 0; i < 5; i++) {
sale();
}
}
//使用同步方法
public void sale() {
if (this.ticket > 0) {
System.out.println(Thread.currentThread().getName()
+ "卖票: " + this.ticket--);
}
}
public static void main(String[] args) {
MyThread mt = new MyThread();
Thread thread1 = new Thread(mt, "售票口一");
Thread thread2 = new Thread(mt, "售票口二");
Thread thread3 = new Thread(mt, "售票口三");
thread1.start();
thread2.start();
thread3.start();
}
}
输出结果:
终于资源共享了。哎哟卧槽,怎么两个售票口都卖了一次票5,仔细思考一下。是不是有可能两个线程是同时执行ticket--的。
好像有道理。这个时候就需要同步代码块上场了。我在操作票的时候就不准你们操作了等我操作完,你们再操作。
先科普一下线程同步
JAVA多线程同步主要依赖于若干方法和关键字
1 wait方法
2 notify方法和notifyAll方法
3 synchronized关键字
4 atomic action(原子操作)
此处针对上面情况使用同步关键字synchronized解决.同步关键字使用有2种方法
1.同步代码块
2.同步方法
同步代码块
使用synchronized关键字进行同步代码块的声明,但是在使用此操作时必须明确的指出到底要锁定的是哪个对象,一般是以当前对象为主.
synchronized(对象){ //一般都是讲this锁定
//锁定对象
}
class MyThread implements Runnable {
private int ticket = 5;
@Override
public void run() {
for (int i = 0; i < 5; i++) {
synchronized (this) {
sale();
}
}
}
//使用同步方法
public void sale() {
if (this.ticket > 0) {
try {
Thread.sleep(200); //休息200毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "卖票: " + this.ticket--);
}
}
public static void main(String[] args) {
MyThread mt = new MyThread();
Thread thread1 = new Thread(mt, "售票口一");
Thread thread2 = new Thread(mt, "售票口二");
Thread thread3 = new Thread(mt, "售票口三");
thread1.start();
thread2.start();
thread3.start();
}
}
输出如下
售票口一卖票: 5
售票口一卖票: 4
售票口一卖票: 3
售票口一卖票: 2
售票口二卖票: 1
总算正常了。不过加休眠是什么鬼,这个呃...因为不休眠线程全给一条线程跑完了。囧~~
你们可以试试休眠不加同步。这边会出很多访问的问题。
最后介绍同步方法
class MyThread implements Runnable {
private int ticket = 5;
@Override
public void run() {
for (int i = 0; i < 5; i++) {
sale();
}
}
//使用同步方法
public synchronized void sale() {
if (this.ticket > 0) {
try {
Thread.sleep(100); //休息200毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "卖票: " + this.ticket--);
}
}
public static void main(String[] args) {
MyThread mt = new MyThread();
Thread thread1 = new Thread(mt, "售票口一");
Thread thread2 = new Thread(mt, "售票口二");
Thread thread3 = new Thread(mt, "售票口三");
thread1.start();
thread2.start();
thread3.start();
}
}
售票口一卖票: 5
售票口一卖票: 4
售票口一卖票: 3
售票口二卖票: 2
售票口二卖票: 1
最后因为加上了线程锁,很容易出现线程阻塞,死锁等问题。
产生死锁的必要条件和如何预防死锁
有问题欢迎探讨!!