一、 线程创建方式

线程创建有二种方式,继承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接口");
	}

}


二、线程状态-生命周期

线程的状态(生命周期)大概可以按照下图表示。

多线程加锁java java多线程加锁的三种方式_同步函数

三 、多线程和锁对象

这里表达的多线程通常是指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 锁设计

如果一个对象是多个线程的共享资源,应该尽可能在线程类中用属性维护起来,同时考虑在该对象对应的类中使用同步(函数或者代码块)加锁。