1. 实现线程的两种方式
class="java">
1. 继承Thread类并重写run方法
public class ThreadTest{
public static void main(String[] args){
Thread1 t1 = new Thread1("first thread");
Thread2 t2 = new Thread2("second thread");
System.out.println(t1.getName());
System.out.println(t2.getName());
t1.start();
t2.start();
}
}
class Thread1 extends Thread{
public Thread1(String name){
super(name);
}
@Override
public void run(){
for(int i = 0; i < 100; i++){
System.out.println("hello world: " + i);
}
}
}
class Thread2 extends Thread{
public Thread2(String name){
super(name);
}
@Override
public void run(){
for(int i = 0; i < 100; i++){
System.out.println("welcome: " + i);
}
}
}
2. 定义实现runnable接口的类进而实现run方法
public class ThreadTest2{
public static void main(String[] args){
// Thread t1 = new Thread(new Runnable()
// {
// @Override
// public void run()
// {
// for(int i = 0; i < 100; i++)
// {
// System.out.println("hello :" + i);
// }
// }
// });
// t1.start();
Thread t1 = new Thread(new MyThread());
t1.start();
Thread t2 = new Thread(new MyThread2());
t2.start();
}
}
class MyThread implements Runnable{
@Override
public void run(){
for(int i = 0; i < 100; i++){
System.out.println("hello :" + i);
}
}
}
class MyThread2 implements Runnable{
@Override
public void run(){
for(int i = 0; i < 100; i++){
System.out.println("welcome: " + i);
}
}
}
2. 线程
1) Thread类也实现了Runnable接口,因此实现了Runnable接口中的run方法;
2) 当生成一个线程对象时,如果没有为其设定名字,那么线程对象的名字将使用如下形式:Thread-number,该number将是自动增加的,并被所有的Thread对象所共享(因为它是static的成员变量)。
3) 当使用第一种方式来生成线程对象时,我们需要重写run方法,因为Thread类的run方法此时什么事情也不做。
4) 当使用第二种方式来生成线程对象时,我们需要实现Runnable接口的run方法,然后使用newThread(newMyThread())(假如MyThread已经实现了Runnable接口)来生成线程对象,这时的线程对象的run方法就会调用MyThread类的run方法,这样我们自己编写的run方法就执行了。
停止线程要重新构造方法,不能使用stop()方法。
3. 线程变量
HelloThread中i定义的位置。
线程的全局变量只有一个,而局部变量每一个都会有一个。
关于成员变量与局部变量:如果一个变量是成员变量,那么多个线程对同一个对象的成员变量进行操作时,他们对该成员变量是彼此影响的(也就是说一个线程对成员变量的改变会影响到另一个线程)。
public class ThreadTest3{
public static void main(String[] args){
Runnable r = new HelloThread();
Thread t1 = new Thread(r);
//r = new HelloThread();
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
}
class HelloThread implements Runnable{
int i;
@Override
public void run(){
int i = 0;
while(true){
System.out.println("number: " + this.i++);
try{
Thread.sleep((long)(Math.random() * 1000));
}
catch (InterruptedException e){
e.printStackTrace();
}
if(50 == this.i){
break;
}
}
}
}
4. Synchronized关键字
当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。
public class FetchMoney{
public static void main(String[] args){
Bank bank = new Bank();
Thread t1 = new MoneyThread(bank); // 柜台
//bank = new Bank();
Thread t2 = new MoneyThread(bank); // 取款机
t1.start();
t2.start();
}
}
class Bank{
private int money = 1000;
public synchronized int getMoney(int number){
if(number < 0){
return -1;
}
else if(number > money){
return -2;
}
else if(money < 0){
return -3;
}
else{
try{
Thread.sleep(1000);
}
catch (InterruptedException e){
e.printStackTrace();
}
money -= number;
System.out.println("left money: " + money);
return number;
}
}
}
class MoneyThread extends Thread{
private Bank bank;
public MoneyThread(Bank bank){
this.bank = bank;
}
@Override
public void run(){
System.out.println(bank.getMoney(800));
}
}
Java中的每个对象都有一个锁(lock)或者叫做监视器(monitor),当访问某个对象的synchronized方法时,表示将该对象上锁,此时其他任何线程都无法再去访问该synchronized方法了,直到之前的那个线程执行方法完毕后(或者是抛出了异常),那么将该对象的锁释放掉,其他线程才有可能再去访问该synchronized方法。
如果一个对象有多个synchronized方法,某一时刻某个线程已经进入到了某个synchronized方法,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任何synchronized方法的。
public class ThreadTest4{
public static void main(String[] args){
Example example = new Example();
Thread t1 = new TheThread(example);
// 区分一个对象还是两个对象
// example = new Example();
Thread t2 = new TheThread2(example);
t1.start();
t2.start();
}
}
class Example{
//static对顺序的影响
public synchronized void execute(){
for(int i = 0; i < 20; i++){
try{
Thread.sleep((long)(Math.random() * 1000));
}
catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("hello: " + i);
}
}
//static对顺序的影响
public synchronized void execute2(){
for(int i = 0; i < 20; i++){
try{
Thread.sleep((long)(Math.random() * 1000));
}
catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("world: " + i);
}
}
}
class TheThread extends Thread{
private Example example;
public TheThread(Example example){
this.example = example;
}
@Override
public void run(){
this.example.execute();
}
}
class TheThread2 extends Thread{
private Example example;
public TheThread2(Example example){
this.example = example;
}
@Override
public void run(){
this.example.execute2();
}
}
1) Static方法是属于类的,不是属于对象的。它对应的对象是class对象。
一个类有两个synchronized static方法,则先执行一个,后执行另一个。
2) 如果某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在的对象所对应的Class对象,因为Java中无论一个类有多少个对象,这些对象会对应唯一一个Class对象,因此当线程分别访问同一个类的两个对象的两个static,synchronized方法时,他们的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始执行。
5. Synchronized块
Synchronized块(比synchronized方法更加细粒度)对object进行上锁(传入的object是否是一个object)。
public class ThreadTest5{
public static void main(String[] args){
Example2 e = new Example2();
TheThread3 t1 = new TheThread3(e);
e = new Example2();
TheThread4 t2 = new TheThread4(e);
t1.start();
t2.start();
}
}
class Example2{
private Object object = new Object();
public void execute(){
synchronized (this){
for (int i = 0; i < 20; i++){
try{
Thread.sleep((long) (Math.random() * 1000));
}
catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("hello: " + i);
}
}
}
public void execute2(){
synchronized(this){
for (int i = 0; i < 20; i++){
try{
Thread.sleep((long) (Math.random() * 1000));
}
catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("world: " + i);
}
}
}
}
class TheThread3 extends Thread{
private Example2 example;
public TheThread3(Example2 example){
this.example = example;
}
@Override
public void run(){
this.example.execute();
}
}
class TheThread4 extends Thread{
private Example2 example;
public TheThread4(Example2 example){
this.example = example;
}
@Override
public void run(){
this.example.execute2();
}
}
两个对象一定的乱序的。
6. 死锁(deadlock)
wait方法,调用必须在synchronized方法或块当中。
public class Sample{
private int number;
public synchronized void increase(){
while (0 != number){
try{
wait();
}
catch (InterruptedException e){
e.printStackTrace();
}
}
number++;
System.out.println(number);
notify();
}
public synchronized void decrease(){
while (0 == number){
try{
wait();
}
catch (InterruptedException e){
e.printStackTrace();
}
}
number--;
System.out.println(number);
notify();
}
}
public class IncreaseThread extends Thread{
private Sample sample;
public IncreaseThread(Sample sample){
this.sample = sample;
}
@Override
public void run(){
for(int i = 0; i < 20; i++){
try{
Thread.sleep((long)(Math.random() * 1000));
}
catch (InterruptedException e){
e.printStackTrace();
}
sample.increase();
}
}
}
public class DecreaseThread extends Thread{
private Sample sample;
public DecreaseThread(Sample sample){
this.sample = sample;
}
@Override
public void run(){
for(int i = 0; i < 20; i++){
try{
Thread.sleep((long)(Math.random() * 1000));
}
catch (InterruptedException e){
e.printStackTrace();
}
sample.decrease();
}
}
}
public class MainTest{
public static void main(String[] args){
Sample sample = new Sample();
Thread t1 = new IncreaseThread(sample);
Thread t2 = new DecreaseThread(sample);
Thread t3 = new IncreaseThread(sample);
Thread t4 = new DecreaseThread(sample);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
4个线程的时候wait之后还需要判断用while不用if。
7. 说明
wait与notify方法都是定义在Object类中,而且是final的,因此会被所有的Java类所继承并且无法重写。这两个方法要求在调用时线程应该已经获得了对象的锁,因此对这两个方法的调用需要放在synchronized方法或块当中。当线程执行了wait方法时,它会释放掉对象的锁。
另一个会导致线程暂停的方法就是Thread类的sleep方法,它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的。
8. 单例模式
对于单例模式(Singleton)来说,如果在getInstance()方法中生成Singleton实例则可能会产生同步问题,即可能会生成两个不同的对象。
public class Singleton{
private static Singleton singleton;
private Singleton(){}
public static Singleton getInstance(){
if (null == singleton){
try{
Thread.sleep((long) (Math.random() * 4000));
}
catch (InterruptedException e){
e.printStackTrace();
}
singleton = new Singleton();
}
return singleton;
}
public static void main(String[] args){
new MyThread().start();
new MyThread().start();
}
}
class MyThread extends Thread{
@Override
public void run(){
System.out.println(Singleton.getInstance());
}
}
运行结果:
code17.Singleton@1fa1bb6
code17.Singleton@1315d34