多线程基础知识第一篇:创建线程3种方式

第一,首先说一下为什么要使用多线程。

线程是大多数操作系统调度的基本单元,一个程序作为一个进程来执行,程序运行过程中能够创建多个线程,而一个线程在一个时刻只能运行在一个处理器核心上。也就是说单线程程序只能使用一个处理器核心,那么加入再多的处理器核心也无法显著提升程序的执行效率。相反,如果该程序使用多线程技术,将计算逻辑分配到多个处理器核心上,就会显著减少程序的处理时间,并且随着更多处理器核心的加入而变得更有效率。

第二,线程的创建。

目前有3种创建线程的方式:

1.继承Thread类的方式:

class SubThread extends Thread {
	private int count;

	public void run() {
		try {
			TimeUnit.MILLISECONDS.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("current thread: " + Thread.currentThread().getName());
	}
}

public class ThreadTest {
	public static void main(String[] args) {
		Thread thread1 = new SubThread();
		thread1.setName("线程1");
		thread1.start();
	}
}
       以上代码,用Thread子类的构造器产生一个Thread子类对象并向上转型为Thread类型。之后可以给线程对象设置名字、设置优先级,然后调用 start()方法启动线程。如果不给线程设置名字,则线程的名字默认为Thread-0、Thread-1这种格式的。如果不给线程设置优先级,则线程的优先级默认是5,即是Thread.NORM_PRIORITY。

       如果想在创建线程的时候就设置名字,则可创建一个String类型的成员变量,然后调用以此变量为参数的构造器就可得到一个线程对象,值得注意的是,必须要在此构造器中调用父类也就是Thread的参数是String类型的构造器:

class SubThread extends Thread {
	private int count;
	private String name;

	public SubThread(String name) {
		super(name);
		this.name = name;
	}

	public SubThread() {
		super();
	}

	public void run() {
		try {
			TimeUnit.MILLISECONDS.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("current thread: " + Thread.currentThread().getName());
	}
}

public class ThreadTest {
	public static void main(String[] args) {
		Thread thread1 = new SubThread("线程1");
		thread1.start();
	}
}
       之前一直以为,继承Thread类创建线程的方式,多个线程不能共享Thread子类的成员变量。其实这是不对的。如果这个成员变量是一般类型(基本类型、String类型、Date类型、集合类型),这种说法是成立的,毕竟每一次调用new SubThread()方法就创建了一个新的对象,这些个成员变量都是不同对象的成员变量。但是如果这个成员变量是自定义的类类型,而且线程对象是调用以此自定义类类型变量为参数的构造器生成的,则此时多个线程可以共享Thread子类的成员变量,毕竟调用有参构造器时传入的参数是同一个:

class Account {
	private int count = 500;

	public void dec() {
		while (count > 1) {
			synchronized (this) {
				count = count - 1;
				System.out.println("current thread: " + Thread.currentThread().getName() + ",count=" + count);
			}
		}
	}
}

class SubThread extends Thread {
	private String name;
	private Account account;

	public SubThread(String name, Account account) {
		super(name);
		this.name = name;
		this.account = account;
	}

	public SubThread() {
		super();
	}

	public void run() {
		account.dec();
	}
}

public class ThreadTest {
	public static void main(String[] args) {
               Account account = new Account();
		Thread thread1 = new SubThread("线程1", account);
		Thread thread2 = new SubThread("线程2", account);
		thread1.start();
		thread2.start();
	}
}
       如上,调用第三方类变量作为参数的构造器来创建线程,当传入同一个第三方类实例时,创建的多个线程就可以共享这个第三方类实例,当然也就共享这个第三方类实例的成员变量了。   

2.实现Runnable接口的方式:

class ThreadImpl implements Runnable {
	private int count;

	@Override
	public void run() {
		while (true) {
			count = count + 1;
			System.out.println("count="+count);
			if (count > 100) {
				break;
			}
		}
	}
}

public class ThreadTest {
	public static void main(String[] args) {
		Runnable target = new ThreadImpl();
		Thread thread1 = new Thread(target);
		thread1.start();
	}
}

       以上,其实是用了Thread(Runnable target) 构造器,传入一个Runnable对象就可以得到一个线程对象。如果想在创建线程的时候给线程设置名字,则可以用另一个构造器Thread(Runnable target, String name) ,第二个参数传线程名字就好了。其实Thread类还有一个public的无参构造器,但是用此无参构造器生成的线程对象调用start()启动后执行的run()方法是Thread类的run()方法,什么也不做,所以此无参构造器实际上没有啥作用。

       如果创建N个线程传的是同一个Runnable对象,则这N个线程共享此Runnable对象的成员变量。

class ThreadImpl implements Runnable {
	private int count;

	@Override
	public void run() {
		while (true) {
			count = count + 1;
			System.out.println("当前线程:" + Thread.currentThread().getName() + ",count:" + count);
			if (count > 100) {
			        break;
			}
		}
	}
}

public class ThreadTest {
	public static void main(String[] args) {
		Runnable target = new ThreadImpl();
		Thread thread1 = new Thread(target, "线程1");
		Thread thread2 = new Thread(target, "线程2");
		thread1.start();
		thread2.start();
	}
}
       以上,用两个线程对同一个变量account进行增长操作,在打印结果中线程1和线程2交叉出现,顺序不一定,account的值从1一直升到101,中间顺序可能紊乱,也能有几条相同,这就是线程不安全。

3.实现Callable接口的方式:

class CallableImpl implements Callable<Integer> {
	private int count;

	@Override
	public Integer call() {
		while (true) {
			count = count + 1;
			System.out.println("当前线程:" + Thread.currentThread().getName()
					+ ",count=" + count);
			if (count > 100) {
				break;
			}
		}
		return count;
	}
}

public class ThreadTest {
	public static void main(String[] args) {
		CallableImpl callableImpl = new CallableImpl();
		FutureTask<Integer> futureTask1 = new FutureTask<Integer>(callableImpl);
		new Thread(futureTask1, "线程1").start();
		try {
			System.out.println("线程1的返回值: " + futureTask1.get());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

      以上,创建了Callable接口的实现类,并重写了Callable的call方法。创建该Callable实现类的对象,并传入到FutureTask的构造器FutureTask(Callable<V> callable)中,得到FutureTask对象。因为FutureTask类实现了Runnable接口,所以该FutureTask对象可以传到Thread的构造器Thread(Runnable target)中,从而得到一个线程对象。同Runnable实现类的run方法体是线程的执行体一样,Callable实现类的call方法体即为线程的执行体。不同的是,call方法是有返回值的,调用FutureTask对象的重载的get()方法可以得到线程的返回值,而至于返回值用不用,则看具体业务。

      以这种方式创建线程,如果想多个线程共享一个成员变量,只需创建一个Callable对象,然后用该Callable对象创建多个FutureTask对象,再创建多个线程即可,这样多个线程就可以共同操作那一个Callable对象的成员变量了。见以下代码:

class CallableImpl implements Callable<Object> {
	private int count;

	@Override
	public Object call() {
		while (true) {
			count = count + 1;
			System.out.println("当前线程:" + Thread.currentThread().getName()
					+ ",count=" + count);
			if (count > 100) {
				break;
			}
		}
		return null;
	}
}

public class ThreadTest {
	public static void main(String[] args) {
		CallableImpl callableImpl = new CallableImpl();
		FutureTask<Object> futureTask1 = new FutureTask<Object>(callableImpl);
		FutureTask<Object> futureTask2 = new FutureTask<Object>(callableImpl);
		new Thread(futureTask1, "线程1").start();
		new Thread(futureTask2, "线程2").start();
	}
}
注意,以上代码也存在线程安全问题。

第三,线程的优先级:

线程的优先级是用1到10的整数表示的,数值越大,表示优先级越高,获得时间片从而被CPU执行的机会越大。

Thread类有3个final的静态变量,Thread.MIN_PRIORITY、Thread.NORM_PRIORITY、Thread.MAX_PRIORITY,值分别是1、5、10,分别对应着线程的最低优先级、普通优先级和最大优先级。值得注意的是,每个线程在new出来之后的优先级都是NORM_PRIORITY,调用getPriority()方法可以得到线程的优先级。而如果要提升或者降低优先级,则只需要调用setPriority(int newPriority)方法即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值