1.线程的创建-继承和接口实现的不同
1.1 接口
1.1.1 代码
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
System.out.println( "Hello World!" );
MyThread mt1 = new MyThread("线程A ") ; // 实例化对象
MyThread mt2 = new MyThread("线程B ") ; // 实例化对象
Thread t1 = new Thread(mt1) ; // 实例化Thread类对象
Thread t2 = new Thread(mt2) ; // 实例化Thread类对象
t1.start() ; // 启动多线程
t2.start() ; // 启动多线程
}
}
class MyThread implements Runnable{ // 实现Runnable接口,作为线程的实现类
private String name ; // 表示线程的名称
public MyThread(String name){
this.name = name ; // 通过构造方法配置name属性
}
public void run(){ // 覆写run()方法,作为线程 的操作主体
for(int i=0;i<5;i++){
System.out.println(name + "运行,i = " + i) ;
}
}
};
1.1.2 运行结果
Hello World!
线程A 运行,i = 0
线程A 运行,i = 1
线程A 运行,i = 2
线程A 运行,i = 3
线程B 运行,i = 0
线程A 运行,i = 4
线程B 运行,i = 1
线程B 运行,i = 2
线程B 运行,i = 3
线程B 运行,i = 4
1.2 继承实现
1.2.1 代码
package xjc.com.testroot2;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
System.out.println( "Hello World!" );
MyThread mt1 = new MyThread("线程A ") ; // 实例化对象
MyThread mt2 = new MyThread("线程B ") ; // 实例化对象
Thread t1 = new Thread(mt1) ; // 实例化Thread类对象
Thread t2 = new Thread(mt2) ; // 实例化Thread类对象
t1.start() ; // 启动多线程
t2.start() ; // 启动多线程
}
}
class MyThread extends Thread{ // 继承Thread类,作为线程的实现类
private String name ; // 表示线程的名称
public MyThread(String name){
this.name = name ; // 通过构造方法配置name属性
}
public void run(){ // 覆写run()方法,作为线程 的操作主体
for(int i=0;i<5;i++){
System.out.println(name + "运行,i = " + i) ;
}
}
};
1.2.2 运行结果
Hello World!
线程A 运行,i = 0
线程A 运行,i = 1
线程A 运行,i = 2
线程A 运行,i = 3
线程B 运行,i = 0
线程A 运行,i = 4
线程B 运行,i = 1
线程B 运行,i = 2
线程B 运行,i = 3
线程B 运行,i = 4
1.3 继承和接口实现的差别
1.3.1代码
package xjc.com.testroot2;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
App app = new App();
app.main1();
try {
Thread.sleep(5000);
}catch(Exception e){
}
app.main2();
}
public void main1() {
System.out.println( "Hello World!" );
MyThread1 mt1 = new MyThread1("线程A ") ; // 实例化对象
MyThread1 mt2 = new MyThread1("线程B ") ; // 实例化对象
Thread t1 = new Thread(mt1) ; // 实例化Thread类对象
Thread t2 = new Thread(mt2) ; // 实例化Thread类对象
t1.start() ; // 启动多线程
t2.start() ; // 启动多线程
}
public void main2() {
System.out.println( "Hello World!" );
MyThread2 mt1 = new MyThread2("线程A ") ; // 实例化对象
MyThread2 mt2 = new MyThread2("线程B ") ; // 实例化对象
Thread t1 = new Thread(mt1) ; // 实例化Thread类对象
Thread t2 = new Thread(mt2) ; // 实例化Thread类对象
t1.start() ; // 启动多线程
t2.start() ; // 启动多线程
}
}
class MyThread1 implements Runnable{ // 实现Runnable接口,作为线程的实现类
private String name ; // 表示线程的名称
private int num;
public MyThread1(String name){
this.name = name ; // 通过构造方法配置name属性
}
public void run(){ // 覆写run()方法,作为线程 的操作主体
for(int i=0;i<5;i++){
System.out.println(name + "运行,i = " + i+" num:"+num++) ;
}
}
};
class MyThread2 extends Thread{ // 继承Thread类,作为线程的实现类
private String name ; // 表示线程的名称
private int num;
public MyThread2(String name){
this.name = name ; // 通过构造方法配置name属性
}
public void run(){ // 覆写run()方法,作为线程 的操作主体
for(int i=0;i<5;i++){
System.out.println(name + "运行,i = " + i+" num:"+num++) ;
}
}
};
运行效果
Hello World!
线程A 运行,i = 0 num:0
线程B 运行,i = 0 num:0
线程A 运行,i = 1 num:1
线程B 运行,i = 1 num:1
线程A 运行,i = 2 num:2
线程B 运行,i = 2 num:2
线程B 运行,i = 3 num:3
线程B 运行,i = 4 num:4
线程A 运行,i = 3 num:3
线程A 运行,i = 4 num:4
Hello World!
线程A 运行,i = 0 num:0
线程A 运行,i = 1 num:1
线程B 运行,i = 0 num:0
线程B 运行,i = 1 num:1
线程B 运行,i = 2 num:2
线程B 运行,i = 3 num:3
线程B 运行,i = 4 num:4
线程A 运行,i = 2 num:2
线程A 运行,i = 3 num:3
线程A 运行,i = 4 num:4
1.3 对比2
1.3.1 代码
package xjc.com.testroot2;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
App app = new App();
app.main1();
try {
Thread.sleep(5000);
}catch(Exception e){
}
app.main2();
}
public void main1() {
System.out.println( "Hello World!" );
MyThread1 my = new MyThread1();
Thread mt1 = new Thread(my) ; // 实例化对象
Thread mt2 = new Thread(my) ; // 实例化对象
Thread t1 = new Thread(mt1) ; // 实例化Thread类对象
Thread t2 = new Thread(mt2) ; // 实例化Thread类对象
t1.start() ; // 启动多线程
t2.start() ; // 启动多线程
}
public void main2() {
System.out.println( "Hello World!" );
MyThread2 my = new MyThread2();
Thread mt1 = new Thread(my) ; // 实例化对象
Thread mt2 = new Thread(my) ; // 实例化对象
Thread t1 = new Thread(mt1) ; // 实例化Thread类对象
Thread t2 = new Thread(mt2) ; // 实例化Thread类对象
t1.start() ; // 启动多线程
t2.start() ; // 启动多线程
}
}
class MyThread1 implements Runnable{ // 实现Runnable接口,作为线程的实现类
private int num;
public void run(){ // 覆写run()方法,作为线程 的操作主体
for(int i=0;i<5;i++){
System.out.println( "num:"+num++) ;
}
}
};
class MyThread2 extends Thread{ // 继承Thread类,作为线程的实现类
private int num;
public void run(){ // 覆写run()方法,作为线程 的操作主体
for(int i=0;i<5;i++){
System.out.println(" num:"+num++) ;
}
}
};
1.3.2 运行效果
Hello World!
num:0
num:2
num:3
num:4
num:1
num:5
num:6
num:7
num:8
num:9
Hello World!
num:0
num:2
num:3
num:4
num:5
num:1
num:6
num:7
num:8
num:9
发现也没有什么区别
1.3 对比结论
通过上面的对比,我们发现根本没有什么实质的差别,看下面的观点
实际上 Thread 类和 Runnable 接口之间在使用上也是有区别的,
如果一个类继承 Thread类,则不适合于多个线程共享资源,
而实现了 Runnable 接口,就可以方便的实现资源的共享。
其实上面强调的差别,只是用法的差别,并不是继承和实现接口的差别。
接口的写法
MyThread2 m=new MyThread2();
new Thread(m).start();
new Thread(m).start();
继承的写法
new MyThread().start();
new MyThread().start();
这本来就是两个不同的写法,确用来说明其差别,根据没有意义。更容易误导。
2 同步以及死锁
2.1 未使用同步锁的结果
2.1.1 代码
package xjc.com.testroot2;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
App app = new App();
app.main1();
try {
Thread.sleep(7000);
}catch(Exception e){
}
app.main2();
}
public void main1() {
System.out.println( "Hello World!" );
MyThread1 my = new MyThread1();
Thread mt1 = new Thread(my) ; // 实例化对象
Thread mt2 = new Thread(my) ; // 实例化对象
Thread t1 = new Thread(mt1) ; // 实例化Thread类对象
Thread t2 = new Thread(mt2) ; // 实例化Thread类对象
t1.start() ; // 启动多线程
t2.start() ; // 启动多线程
}
public void main2() {
System.out.println( "Hello World!" );
MyThread2 my = new MyThread2();
Thread mt1 = new Thread(my) ; // 实例化对象
Thread mt2 = new Thread(my) ; // 实例化对象
Thread t1 = new Thread(mt1) ; // 实例化Thread类对象
Thread t2 = new Thread(mt2) ; // 实例化Thread类对象
t1.start() ; // 启动多线程
t2.start() ; // 启动多线程
}
}
class MyThread1 implements Runnable{ // 实现Runnable接口,作为线程的实现类
private int num;
public void run(){ // 覆写run()方法,作为线程 的操作主体
for(int i=0;i<5;i++){
System.out.println( "num:"+num++) ;
try {
Thread.sleep(800);
}catch(Exception e){
}
}
}
};
class MyThread2 extends Thread{ // 继承Thread类,作为线程的实现类
private int num;
public void run(){ // 覆写run()方法,作为线程 的操作主体
for(int i=0;i<5;i++){
System.out.println(" num:"+num++) ;
try {
Thread.sleep(800);
}catch(Exception e){
}
}
}
};
2.1.2运行结果
Hello World!
num:0
num:1
num:2
num:2
num:3
num:3
num:4
num:4
num:6
num:5
Hello World!
num:0
num:1
num:2
num:2
num:3
num:3
num:4
num:4
num:5
num:5
发现有点乱,有些运算未生效,最大值应是0~5*2 = 9,这就是多线程处理数据容易产生的问题。
2.2 使用同步锁
2.2.1 代码
package xjc.com.testroot2;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
App app = new App();
app.main1();
try {
Thread.sleep(10000);
}catch(Exception e){
}
app.main2();
}
public void main1() {
System.out.println( "使用同步锁" );
MyThread1 my = new MyThread1();
Thread mt1 = new Thread(my) ; // 实例化对象
Thread mt2 = new Thread(my) ; // 实例化对象
Thread t1 = new Thread(mt1) ; // 实例化Thread类对象
Thread t2 = new Thread(mt2) ; // 实例化Thread类对象
t1.start() ; // 启动多线程
t2.start() ; // 启动多线程
}
public void main2() {
System.out.println( "未使用同步锁" );
MyThread2 my = new MyThread2();
Thread mt1 = new Thread(my) ; // 实例化对象
Thread mt2 = new Thread(my) ; // 实例化对象
Thread t1 = new Thread(mt1) ; // 实例化Thread类对象
Thread t2 = new Thread(mt2) ; // 实例化Thread类对象
t1.start() ; // 启动多线程
t2.start() ; // 启动多线程
}
}
class MyThread1 implements Runnable{ // 实现Runnable接口,作为线程的实现类
private int num;
public void run(){ // 覆写run()方法,作为线程 的操作主体
for(int i=0;i<5;i++){
synchronized(this) {
System.out.println( "num:"+num++) ;
try {
Thread.sleep(800);
}catch(Exception e){
}
}
}
}
};
class MyThread2 extends Thread{ // 继承Thread类,作为线程的实现类
private int num;
public void run(){ // 覆写run()方法,作为线程 的操作主体
for(int i=0;i<5;i++){
System.out.println(" num:"+num++) ;
try {
Thread.sleep(800);
}catch(Exception e){
}
}
}
};
2.2.2 运行效果
使用同步锁
num:0
num:1
num:2
num:3
num:4
num:5
num:6
num:7
num:8
num:9
未使用同步锁
num:0
num:1
num:2
num:3
num:4
num:5
num:6
num:6
num:7
num:7
发现问题了吧,使用同步锁解决了运算错误的问题。那么锁上了,什么时候开锁呢,这里不涉及到if else依赖数据的处理。但是如果有这些,就会导致,锁了之后永远打不开的情况,锁上之后,再次运行时需要状态的,如果两个现在都不未得到运行的状态,那么程序就产生了死锁。
2.3 死锁
2.3.1 代码
package xjc.com.testroot2;
/**
* Hello world!
*
*/
public class App
{
public static void main( String[] args )
{
App app = new App();
app.main1();
try {
Thread.sleep(10000);
}catch(Exception e){
}
app.main2();
}
public void main1() {
System.out.println( "死锁发生" );
MyThread1 my = new MyThread1();
Thread mt1 = new Thread(my) ; // 实例化对象
Thread mt2 = new Thread(my) ; // 实例化对象
Thread t1 = new Thread(mt1) ; // 实例化Thread类对象
Thread t2 = new Thread(mt2) ; // 实例化Thread类对象
t1.start() ; // 启动多线程
t2.start() ; // 启动多线程
}
public void main2() {
System.out.println( "死锁不发生" );
MyThread2 my = new MyThread2();
Thread mt1 = new Thread(my) ; // 实例化对象
Thread mt2 = new Thread(my) ; // 实例化对象
Thread t1 = new Thread(mt1) ; // 实例化Thread类对象
Thread t2 = new Thread(mt2) ; // 实例化Thread类对象
t1.start() ; // 启动多线程
t2.start() ; // 启动多线程
}
}
class MyThread1 implements Runnable{ // 实现Runnable接口,作为线程的实现类
private int num=0;
public void run(){ // 覆写run()方法,作为线程 的操作主体
for(int i=0;i<5;i++){
synchronized(this) {
if(num==0) {
System.out.println( "num:"+num++) ;
}
try {
Thread.sleep(800);
}catch(Exception e){
}
}
}
}
};
class MyThread2 extends Thread{ // 继承Thread类,作为线程的实现类
private int num;
public void run(){ // 覆写run()方法,作为线程 的操作主体
for(int i=0;i<5;i++){
System.out.println(" num:"+num++) ;
try {
Thread.sleep(800);
}catch(Exception e){
}
}
}
};
2.3.2 运行效果
死锁发生
num:0
死锁不发生
num:0
num:1
num:2
num:2
num:3
num:3
num:4
num:5
num:6
num:7
这里用了一个最简单,最直接,最暴力的方式设置了一个死锁,目的是告诉大家一个结论,死锁是啥,没啥太深奥的东西,就是该执行的代码进步去了。永远无法执行,这就是死锁。死锁通常是做线程共通控制数据状态,状态间的依赖出问题了,才会导致。这里的目标就是弄明白,什么是死锁。